package blockchain.consensus.pbft;

import blockchain.dao.DAOService;
import blockchain.dao.rocksDB.RocksdbDAO;
import blockchain.entity.MerkleTree.MerkleTree;
import blockchain.entity.block.Block;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
import org.rocksdb.RocksDBException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.testng.util.Strings;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @Author: 李浩然
 * @Date: 2020/12/14 2:21 下午
 */
@Component
public class P2pPointPbftServer {
    private Logger logger = LoggerFactory.getLogger(P2pPointPbftServer.class);
    //本机端口号
    private int port = 7001;
    //Web缓存器
    private List<WebSocket> localSockets = new CopyOnWriteArrayList<>();

    @Autowired
    private DAOService daoService;

    public List<WebSocket> getLocalSockets() {
        return localSockets;
    }

    public void setLocalSockets(List<WebSocket> localSockets) {
        this.localSockets = localSockets;
    }

    /**
     * 初始化P2P 服务端
     */
    @Bean
    public void initServer() {
        final WebSocketServer socketServer = new WebSocketServer(new InetSocketAddress(port)) {
            @Override
            public void onOpen(WebSocket webSocket, ClientHandshake clientHandshake) {
                //连接成功时触发
                sendMessage(webSocket, "服务端成功创建连接");
                //当创建成功WebSocket连接时，将该连接加入连接池
                localSockets.add(webSocket);
            }

            @Override
            public void onClose(WebSocket webSocket, int i, String s, boolean b) {
                logger.info(webSocket.getRemoteSocketAddress() + "客户端与服务端断开连接");
                //当客户端断开WebSocket连接时，将该连接加入连接池
                localSockets.remove(webSocket);
            }

            @Override
            public void onMessage(WebSocket webSocket, String msg) {
                //收到消息时触发
                logger.info("服务端收到客户端消息:" + msg);
                //区块写入后不再发送
                if (msg.equals("客户端开始写入区块")) {
                    return;
                }
                //如果收到不是JSON数据，说明仍然在创立链接，目前连接创立完毕，发起投票
                if (!msg.startsWith("{")) {
                    VoteInfo vi = createVoteInfo(VoteEnum.PREPREPARE, msg);
                    sendMessage(webSocket, JSON.toJSONString(vi));
                    logger.info("服务端发送到客户端pbft消息：" + JSON.toJSONString(vi));
                    return;
                }
                //如果是JSON化数据，说明已经进入PBFT投票阶段
                JSONObject json = JSON.parseObject(msg);
                if (!json.containsKey("code")) {
                    logger.info("未收到投票状态码！！！");
                }
                int code = json.getIntValue("code");
                if (code == VoteEnum.PREPARE.getCode()) {
                    //校验哈希
                    VoteInfo voteInfo = JSON.parseObject(msg, VoteInfo.class);
                    if (!voteInfo.getHash().equals(new MerkleTree(voteInfo.getList()).getRoot().getHash())) {
                        logger.info("未通过Merkle树Hash校验");
                        return;
                    }
                    //若校验成功，进入下一个状态
                    voteInfo.setCode(VoteEnum.COMMIT.getCode());
                    sendMessage(webSocket, JSON.toJSONString(voteInfo));
                    logger.info("服务端发送到客户端pbft消息：" + JSON.toJSONString(voteInfo));
                }
            }

            @Override
            public void onError(WebSocket webSocket, Exception e) {
                //连接发生错误时触发，紧接着触发onClose方法
                logger.info(webSocket.getRemoteSocketAddress() + "客户端连接错误");
                localSockets.remove(webSocket);
            }

            @Override
            public void onStart() {
                logger.info("WebSocket服务端启动");
            }
        };
        socketServer.start();
        logger.info("服务端监听socketServer端口：" + port);
    }

    private VoteInfo createVoteInfo(VoteEnum ve, String value) {
        VoteInfo vi = new VoteInfo();
        vi.setCode(ve.getCode());
        List<String> contents = new ArrayList<>();
        contents.add(value);
        String hash = new MerkleTree(contents).getRoot().getHash();
        vi.setList(contents);
        vi.setHash(hash);

        Block block = new Block();
        return vi;
    }

/*    private VoteInfo getVotoInfo() {
        if(daoService.get()==null)
    }*/

    /**
     * 向客户端发送消息
     *
     * @param webSocket
     * @param message
     */
    public void sendMessage(WebSocket webSocket, String message) {
        logger.info("发送给" + webSocket.getRemoteSocketAddress().getPort() + "的消息是:" + message);
        webSocket.send(message);
    }

    /**
     * 向所有连接到本机的客户端广播消息
     *
     * @param message
     */
    public void broadcast(String message) {
        if (localSockets.size() == 0 || Strings.isNullOrEmpty(message)) {
            return;
        }
        logger.info("开始广播");
        for (WebSocket socket : localSockets) {
            this.sendMessage(socket, message);
        }
        logger.info("广播结束");
    }
}
